/**
 * @file start.c
 * @date 23.05.08
 * @author lyp xin
 * @brief 一系列系统信息的初始化，主要是CSR以及各种上下文信息和处理程序等
 * @version 0.1
 * @status Stable
*/

#include "types.h"
#include "param.h"
#include "riscv.h"
#include "defs.h"
#include "memlayout.h"

void main();
/*
* 定时器初始化函数，单个进程可能不需要定时器中断，以下有关定时器的内容都将注释
*/
void timerinit();

// CPU栈空间
__attribute__((aligned(16))) char stack0[4096 * NCPU];

// 用于存储机器模式下的定时器中断的一些信息
uint64 timer_scratch[NCPU][5];

// 定义中断处理函数，声明在kernelvec.S中
extern void timervec();


/**
 * @brief entry.S跳转到这里，设置一些必要的寄存器和参数，然后跳转到 "main" 函数
*/
void
start()
{
	// 将M Previous Privilege mode设置为Supervisor，为了后面的mret指令做准备
	unsigned long x = r_mstatus();
	x &= ~MSTATUS_MPP_MASK;
	x |= MSTATUS_MPP_S;
	w_mstatus(x);

	// 设置为main函数的地址，为了后面的mret指令做准备
	w_mepc((uint64)main);

	// 禁用分页机制
	w_satp(0);

	// 将所有的中断和异常委托给Supervisor模式处理
	w_medeleg(0xffff);
	w_mideleg(0xffff);
	w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);

	// 配置物理内存保护，使得Supervisor模式可以访问所有物理内存
	w_pmpaddr0(0x3fffffffffffffull);
	w_pmpcfg0(0xf);

	// 请求时钟中断
	// timerinit();

	// 将每个CPU的hartid保存在其tp寄存器中，为了后面的cpuid()函数做准备
	int id = r_mhartid();
	w_tp(id);

	// 切换到Supervisor模式并跳转到main()函数
	__asm__ __volatile__("mret");
}

/**
 * @brief 计时器中断初始化
*/
void 
timeinit()
{
	// 给每个CPU单独设置一个计时器中断
	int id = r_mhartid();

	int interval = 1000000;	// 每个周期大约0.1秒
	*(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval;

	uint64 *scratch = &timer_scratch[id][0];
	// srcatch[0..2]用来处理计时器中断时临时保存寄存器
	scratch[3] = CLINT_MTIMECMP(id);
	scratch[4] = interval;
	w_mscratch((uint64)scratch);

	// 设置机器模式的trap处理程序
	w_mtvec((uint64)timervec);

	// 使能机器模式中断
	w_mstatus(r_mstatus() | MSTATUS_MIE);

	// 使能计时器中断
	w_mie(r_mie() | MIE_MTIE);
}